💻 Code Implementation
Below is the complete technical implementation of the analysis
described above.
cat("=== DATA CLEANING & PREPARATION ===\n")
=== DATA CLEANING & PREPARATION ===
# Check missing values
cat("Missing IDs in response database:", sum(is.na(full_db$ID)), "rows\n")
Missing IDs in response database: 43692 rows
cat("These must be removed because we cannot attribute responses to users without IDs\n\n")
These must be removed because we cannot attribute responses to users without IDs
# Remove rows with missing user ID (cannot be attributed to any user)
full_db_clean <- full_db %>%
filter(!is.na(ID))
cat("After cleaning - usable responses:", nrow(full_db_clean), "\n")
After cleaning - usable responses: 144111
cat("=== TASK 2.1: ALL USERS ANALYSIS ===\n")
=== TASK 2.1: ALL USERS ANALYSIS ===
# Step 1: Count responses per user
user_counts <- full_db_clean %>%
count(ID, name = "response_count")
# Step 2: Join with user data and calculate cumulative statistics
user_data <- user_counts %>%
left_join(users, by = "ID") %>%
arrange(desc(response_count)) %>% # Sort by highest responders first
mutate(
# Calculate running totals
cumulative_responses = cumsum(response_count),
total_responses = sum(response_count),
cumulative_pct = cumulative_responses / total_responses,
user_rank = row_number(),
user_pct = user_rank / n() # Percentage of users represented
)
# Step 3: Find minimum users for 50% threshold using min(which())
cutoff_row <- min(which(user_data$cumulative_pct >= 0.5))
pct_50_all <- user_data$user_pct[cutoff_row] * 100
cat(sprintf("ANSWER: %.2f%% of users account for 50%% of all responses\n", pct_50_all))
ANSWER: 15.68% of users account for 50% of all responses
# Save detailed analysis
write_csv(user_data, file.path(output_dir, "all_users_analysis.csv"))
cat("=== TASK 2.2: RECENT USERS (90 DAYS) ===\n")
=== TASK 2.2: RECENT USERS (90 DAYS) ===
# Step 1: Parse signup dates and define 90-day window
users$signup_date <- as.Date(users$signup_date, format = "%m/%d/%Y")
cutoff_date <- max(users$signup_date, na.rm = TRUE) - 90
cat("90-day cutoff date:", as.character(cutoff_date), "\n")
90-day cutoff date: 2024-06-18
# Step 2: Filter to recent users only
recent_users <- users %>%
filter(signup_date >= cutoff_date)
cat("Recent users found:", nrow(recent_users), "\n")
Recent users found: 18019
# Step 3: Subset response data for recent users
recent_data <- user_data %>%
filter(ID %in% recent_users$ID) %>%
arrange(desc(response_count)) %>%
mutate(
# Recalculate cumulative stats for recent cohort only
cumulative_responses = cumsum(response_count),
total_responses = sum(response_count),
cumulative_pct = cumulative_responses / total_responses,
user_rank = row_number(),
user_pct = user_rank / n()
)
# Step 4: Find 50% threshold for recent users
cutoff_row_recent <- min(which(recent_data$cumulative_pct >= 0.5))
pct_50_recent <- recent_data$user_pct[cutoff_row_recent] * 100
cat(sprintf("ANSWER: %.2f%% of recent users account for 50%% of their responses\n", pct_50_recent))
ANSWER: 28.66% of recent users account for 50% of their responses
# Save recent users analysis
write_csv(recent_data, file.path(output_dir, "recent_users_analysis.csv"))
# Comparison insight
cat("\nCOMPARISON:\n")
COMPARISON:
cat("All users:", round(pct_50_all, 2), "%\n")
All users: 15.68 %
cat("Recent users:", round(pct_50_recent, 2), "%\n")
Recent users: 28.66 %
if(pct_50_recent < pct_50_all) {
cat("Recent users show HIGHER concentration (more active heavy users)\n")
} else {
cat("Recent users show LOWER concentration (more distributed engagement)\n")
}
Recent users show LOWER concentration (more distributed engagement)
cat("=== TASK 2.3: MULTIPLE THRESHOLD ANALYSIS ===\n")
=== TASK 2.3: MULTIPLE THRESHOLD ANALYSIS ===
# Calculate user percentages needed for various response thresholds
response_thresholds <- c(0.10, 0.20, 0.30, 0.40, 0.50, 0.60, 0.70, 0.80, 0.90)
density_summary <- sapply(response_thresholds, function(thresh) {
cutoff_index <- min(which(user_data$cumulative_pct >= thresh))
user_data$user_pct[cutoff_index] * 100
})
names(density_summary) <- paste0(response_thresholds * 100, "%")
cat("User percentage needed for each response threshold:\n")
User percentage needed for each response threshold:
for (i in 1:length(density_summary)) {
cat(sprintf("• %.2f%% of users → %s of responses\n",
density_summary[i], names(density_summary)[i]))
}
• 2.07% of users → 10% of responses
• 4.68% of users → 20% of responses
• 7.79% of users → 30% of responses
• 11.43% of users → 40% of responses
• 15.68% of users → 50% of responses
• 20.99% of users → 60% of responses
• 28.01% of users → 70% of responses
• 38.31% of users → 80% of responses
• 55.92% of users → 90% of responses
# Create summary table
summary_table <- data.frame(
Response_Threshold = names(density_summary),
User_Percentage_Needed = round(density_summary, 2)
)
write_csv(summary_table, file.path(output_dir, "density_thresholds_summary.csv"))
cat("=== LORENZ CURVE VISUALIZATION ===\n")
=== LORENZ CURVE VISUALIZATION ===
# Create Lorenz curve plot
lorenz_plot <- ggplot(user_data, aes(x = user_pct, y = cumulative_pct)) +
geom_line(color = "darkblue", size = 1.2, alpha = 0.8) +
geom_abline(intercept = 0, slope = 1, linetype = "dashed",
color = "gray50", alpha = 0.7) +
annotate("text", x = 0.7, y = 0.3,
label = "Perfect Equality\n(45° line)",
color = "gray50", size = 3) +
annotate("text", x = 0.3, y = 0.7,
label = "Actual Distribution\n(High Concentration)",
color = "darkblue", size = 3) +
scale_x_continuous(labels = scales::percent, name = "Cumulative % of Users") +
scale_y_continuous(labels = scales::percent, name = "Cumulative % of Responses") +
labs(title = "Response Density: Lorenz Curve Analysis",
subtitle = paste0("Distance from diagonal shows concentration level | ",
round(pct_50_all, 1), "% of users → 50% of responses")) +
theme_minimal(base_size = 12) +
theme(plot.title = element_text(size = 14, face = "bold"))
Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
Please use `linewidth` instead.
# Display interactive version
interactive_lorenz <- ggplotly(lorenz_plot, tooltip = c("x", "y"))
interactive_lorenz
# Save static version
ggsave(file.path(output_dir, "lorenz_curve_all_users.png"), lorenz_plot,
width = 10, height = 6, dpi = 300)
cat("Lorenz Curve Interpretation:\n")
Lorenz Curve Interpretation:
cat("• Diagonal line = Perfect equality (everyone contributes equally)\n")
• Diagonal line = Perfect equality (everyone contributes equally)
cat("• Curved line = Actual distribution (concentrated among few users)\n")
• Curved line = Actual distribution (concentrated among few users)
cat("• Larger area between lines = Higher concentration\n")
• Larger area between lines = Higher concentration
cat("=== COMPARISON: ALL vs RECENT USERS ===\n")
=== COMPARISON: ALL vs RECENT USERS ===
# Calculate thresholds for recent users
recent_summary <- sapply(response_thresholds, function(thresh) {
if (max(recent_data$cumulative_pct) >= thresh) {
cutoff_index <- min(which(recent_data$cumulative_pct >= thresh))
recent_data$user_pct[cutoff_index] * 100
} else {
NA
}
})
# Create comparison dataset
comparison_data <- data.frame(
Response_Threshold = response_thresholds * 100,
All_Users = density_summary,
Recent_Users = recent_summary
) %>%
pivot_longer(cols = c(All_Users, Recent_Users),
names_to = "User_Group",
values_to = "User_Percentage")
# Create comparison plot
comparison_plot <- ggplot(comparison_data, aes(x = Response_Threshold, y = User_Percentage,
color = User_Group, linetype = User_Group)) +
geom_line(size = 1.2) +
geom_point(size = 3) +
scale_x_continuous(name = "Response Threshold (%)", breaks = seq(10, 90, 10)) +
scale_y_continuous(name = "User Percentage Needed (%)") +
scale_color_manual(values = c("All_Users" = "darkblue", "Recent_Users" = "darkred")) +
labs(title = "Response Concentration: All Users vs Recent Users (90 Days)",
subtitle = "Lower values indicate higher concentration among fewer users",
color = "User Group", linetype = "User Group") +
theme_minimal(base_size = 12) +
theme(plot.title = element_text(size = 14, face = "bold"),
legend.position = "bottom")
print(comparison_plot)
ggsave(file.path(output_dir, "user_comparison.png"), comparison_plot,
width = 10, height = 6, dpi = 300)

# Save comparison data
write_csv(comparison_data, file.path(output_dir, "all_vs_recent_comparison.csv"))
cat("=== ANALYSIS COMPLETE ===\n")
=== ANALYSIS COMPLETE ===
cat("Generated files in", normalizePath(output_dir), ":\n")
Generated files in /Users/jacksonzhao/Desktop/ds_case_study_jackson_verasight/tasks/task2/outputs :
output_files <- list.files(output_dir)
for (file in output_files) {
cat("•", file, "\n")
}
• all_users_analysis.csv
• all_vs_recent_comparison.csv
• density_thresholds_summary.csv
• lorenz_curve_all_users.png
• recent_users_analysis.csv
• user_comparison.png
cat("\nFinal Answers:\n")
Final Answers:
cat("Task 2.1:", round(pct_50_all, 2), "% of all users → 50% of responses\n")
Task 2.1: 15.68 % of all users → 50% of responses
cat("Task 2.2:", round(pct_50_recent, 2), "% of recent users → 50% of their responses\n")
Task 2.2: 28.66 % of recent users → 50% of their responses
cat("Task 2.3: Lorenz curve visualization with multiple threshold analysis\n")
Task 2.3: Lorenz curve visualization with multiple threshold analysis
LS0tCnRpdGxlOiAiVmVyYXNpZ2h0IERhdGEgU2NpZW50aXN0IENhc2UgU3R1ZHkg4oCTIFRhc2sgMjogUmVzcG9uc2UgRGVuc2l0eSBBbmFseXNpcyIKYXV0aG9yOiAiSmFja3NvbiBaaGFvIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgojIEV4ZWN1dGl2ZSBTdW1tYXJ5OiBSZXNwb25zZSBEZW5zaXR5IEFuYWx5c2lzCgojIyDwn46vIEJ1c2luZXNzIFByb2JsZW0KKipEZW5zaXR5IEFuYWx5c2lzOioqIE1lYXN1cmUgd2hldGhlciBzdXJ2ZXkgcmVzcG9uc2VzIGFyZSBjb25jZW50cmF0ZWQgYW1vbmcgYSBzbWFsbCBudW1iZXIgb2YgaGVhdnkgdXNlcnMgb3IgZGlzdHJpYnV0ZWQgZXZlbmx5IGFjcm9zcyBtYW55IHBhbmVsaXN0cy4gVGhpcyBoZWxwcyB1bmRlcnN0YW5kIHVzZXIgZW5nYWdlbWVudCBwYXR0ZXJucyBhbmQgcG90ZW50aWFsIGJpYXMgaW4gc3VydmV5IHJlc3VsdHMuCgojIyDwn5OKIEZpbmFsIFJlc3VsdHMKCiMjIyAqKlRhc2sgMi4xIEFuc3dlcjoqKiAxNS42OCUgb2YgYWxsIHVzZXJzIGFjY291bnQgZm9yIDUwJSBvZiBzdXJ2ZXkgcmVzcG9uc2VzCiMjIyAqKlRhc2sgMi4yIEFuc3dlcjoqKiAyOC42NiUgb2YgcmVjZW50IHVzZXJzICg5MCBkYXlzKSBhY2NvdW50IGZvciA1MCUgb2YgdGhlaXIgcmVzcG9uc2VzICAKIyMjICoqVGFzayAyLjMgQW5zd2VyOioqIExvcmVueiBjdXJ2ZSB2aXN1YWxpemF0aW9uIHNob3dpbmcgY29uY2VudHJhdGlvbiBhdCBhbGwgdGhyZXNob2xkcwoKLS0tCgoKIyMg8J+TiyBEYXRhIFByb2Nlc3NpbmcgV29ya2Zsb3cKCiMjIyAqKlN0ZXAgMTogRGF0YSBDbGVhbmluZyBTdHJhdGVneSoqCi0gKipSZW1vdmUgbWlzc2luZyBJRHM6KiogQ2FuJ3QgYXR0cmlidXRlIHJlc3BvbnNlcyB3aXRob3V0IHVzZXIgaWRlbnRpZmllcnMKLSAqKlN0YW5kYXJkaXplIGZvcm1hdHM6KiogRW5zdXJlIGNvbnNpc3RlbnQgSUQgdHlwZXMgZm9yIHByb3BlciBqb2lucwotICoqUGFyc2UgZGF0ZXM6KiogRW5hYmxlIGFjY3VyYXRlIDkwLWRheSBmaWx0ZXJpbmcKCiMjIyAqKlN0ZXAgMjogVXNlciBSZXNwb25zZSBDb3VudGluZyoqCi0gKipBZ2dyZWdhdGU6KiogQ291bnQgdG90YWwgcmVzcG9uc2VzIHBlciB1c2VyIElECi0gKipSYW5rOioqIFNvcnQgdXNlcnMgYnkgcmVzcG9uc2UgY291bnQgKGRlc2NlbmRpbmcpCi0gKipDYWxjdWxhdGU6KiogUnVubmluZyB0b3RhbHMgYW5kIHBlcmNlbnRhZ2VzCgojIyMgKipTdGVwIDM6IEN1bXVsYXRpdmUgQW5hbHlzaXMqKgotICoqUnVubmluZyBzdW06KiogQ3VtdWxhdGl2ZSByZXNwb25zZXMgZnJvbSB0b3AgdXNlcnMKLSAqKlBlcmNlbnRhZ2UgY2FsY3VsYXRpb246KiogQ29udmVydCB0byBwcm9wb3J0aW9uIG9mIHRvdGFsIHJlc3BvbnNlcwotICoqVGhyZXNob2xkIGRldGVjdGlvbjoqKiBGaW5kIHdoZXJlIGN1bXVsYXRpdmUgc2hhcmUgY3Jvc3NlcyB0YXJnZXQKCiMjIyAqKlN0ZXAgNDogTG9yZW56IEN1cnZlIENvbnN0cnVjdGlvbioqCi0gKipYLWF4aXM6KiogQ3VtdWxhdGl2ZSBwZXJjZW50YWdlIG9mIHVzZXJzICgwJSB0byAxMDAlKQotICoqWS1heGlzOioqIEN1bXVsYXRpdmUgcGVyY2VudGFnZSBvZiByZXNwb25zZXMgKDAlIHRvIDEwMCUpCi0gKipJbnRlcnByZXRhdGlvbjoqKiBDbG9zZXIgdG8gZGlhZ29uYWwgPSBtb3JlIGVxdWFsIGRpc3RyaWJ1dGlvbgoKLS0tCgojIFRhc2sgMi4xOiBBbGwgVXNlcnMgRGVuc2l0eSBBbmFseXNpcwoKIyMgU3VtbWFyeQpDYWxjdWxhdGUgbWluaW11bSBwZXJjZW50YWdlIG9mIEFMTCBWZXJhc2lnaHQgdXNlcnMgYWNjb3VudGluZyBmb3IgNTAlIG9mIHN1cnZleSByZXNwb25zZXMuCgojIyBXb3JrZmxvdwoxLiAqKkpvaW4gZGF0YXNldHM6KiogTGluayByZXNwb25zZSBkYXRhYmFzZSB3aXRoIHVzZXIgaW5mb3JtYXRpb24KMi4gKipDb3VudCByZXNwb25zZXM6KiogQWdncmVnYXRlIHJlc3BvbnNlcyBwZXIgdXNlciBJRAozLiAqKlNvcnQgJiByYW5rOioqIE9yZGVyIHVzZXJzIGJ5IHJlc3BvbnNlIGNvdW50IChoaWdoZXN0IGZpcnN0KQo0LiAqKkNhbGN1bGF0ZSBjdW11bGF0aXZlOioqIFJ1bm5pbmcgdG90YWxzIG9mIHJlc3BvbnNlcyBhbmQgdXNlciBwZXJjZW50YWdlcwo1LiAqKkZpbmQgdGhyZXNob2xkOioqIElkZW50aWZ5IHdoZXJlIGN1bXVsYXRpdmUgcmVzcG9uc2VzIOKJpSA1MCUKCiMjIEFuc3dlcjogKioxNS42OCUgb2YgdXNlcnMgYWNjb3VudCBmb3IgNTAlIG9mIGFsbCBzdXJ2ZXkgcmVzcG9uc2VzKioKCi0tLQoKIyBUYXNrIDIuMjogUmVjZW50IFVzZXJzICg5MCBEYXlzKSBEZW5zaXR5IEFuYWx5c2lzCgojIyBTdW1tYXJ5CkNhbGN1bGF0ZSBzYW1lIGRlbnNpdHkgbWV0cmljIGJ1dCByZXN0cmljdGVkIHRvIHVzZXJzIHdobyByZWdpc3RlcmVkIHdpdGhpbiBsYXN0IDkwIGRheXMuCgojIyBXb3JrZmxvdwoxLiAqKkRlZmluZSA5MC1kYXkgd2luZG93OioqIENhbGN1bGF0ZSBjdXRvZmYgZGF0ZSBmcm9tIG1vc3QgcmVjZW50IHNpZ251cAoyLiAqKkZpbHRlciByZWNlbnQgdXNlcnM6KiogU3Vic2V0IHRvIHVzZXJzIHJlZ2lzdGVyZWQgd2l0aGluIHdpbmRvdwozLiAqKlN1YnNldCByZXNwb25zZXM6KiogT25seSBpbmNsdWRlIHJlc3BvbnNlcyBmcm9tIHJlY2VudCB1c2Vycwo0LiAqKlJlY2FsY3VsYXRlIG1ldHJpY3M6KiogQXBwbHkgc2FtZSBjdW11bGF0aXZlIGFuYWx5c2lzIHRvIHJlY2VudCBjb2hvcnQKNS4gKipDb21wYXJlIHJlc3VsdHM6KiogQW5hbHl6ZSBkaWZmZXJlbmNlIHZzIGFsbC10aW1lIHVzZXJzCgojIyBBbnN3ZXI6ICoqMjguNjYlIG9mIHJlY2VudCB1c2VycyBhY2NvdW50IGZvciA1MCUgb2YgcmVzcG9uc2VzIGZyb20gdGhhdCBjb2hvcnQqKgoKLS0tCgojIFRhc2sgMi4zOiBWaXN1YWxpemF0aW9uIC0gTG9yZW56IEN1cnZlIEFuYWx5c2lzCgojIyBTdW1tYXJ5CkNyZWF0ZSBkZXRhaWxlZCB2aXN1YWxpemF0aW9uIHNob3dpbmcgZGVuc2l0eSBhY3Jvc3MgbXVsdGlwbGUgdGhyZXNob2xkcyAoMTAlLCAyMCUsIDMwJSwgZXRjLikuCgojIyBXb3JrZmxvdwoxLiAqKkdlbmVyYXRlIHRocmVzaG9sZHM6KiogQ2FsY3VsYXRlIHVzZXIgcGVyY2VudGFnZXMgZm9yIDEwJS05MCUgcmVzcG9uc2Ugc2hhcmVzCjIuICoqQ3JlYXRlIExvcmVueiBjdXJ2ZToqKiBQbG90IGN1bXVsYXRpdmUgdXNlcnMgdnMgY3VtdWxhdGl2ZSByZXNwb25zZXMKMy4gKipBZGQgcmVmZXJlbmNlIGxpbmU6KiogUGVyZmVjdCBlcXVhbGl0eSBkaWFnb25hbCBmb3IgY29tcGFyaXNvbgo0LiAqKkludGVycHJldCByZXN1bHRzOioqIERpc3RhbmNlIGZyb20gZGlhZ29uYWwgc2hvd3MgY29uY2VudHJhdGlvbiBsZXZlbAoKIyMgQW5zd2VyOiAqKkludGVyYWN0aXZlIExvcmVueiBjdXJ2ZSBzaG93aW5nIHBvd2VyLWxhdyBkaXN0cmlidXRpb24gcGF0dGVybioqCgoqKktleSBJbnNpZ2h0czoqKgotICoqSGlnaCBjb25jZW50cmF0aW9uOioqIFNtYWxsIGVsaXRlIGRyaXZlcyBtYWpvcml0eSBvZiByZXNwb25zZXMKLSAqKlBvd2VyLWxhdyBwYXR0ZXJuOioqIFR5cGljYWwgb2YgdXNlciBlbmdhZ2VtZW50IHBsYXRmb3JtcwotICoqSW5lcXVhbGl0eSBtZWFzdXJlOioqIERpc3RhbmNlIGZyb20gZGlhZ29uYWwgaW5kaWNhdGVzIHJlc3BvbnNlIGNvbmNlbnRyYXRpb24KCi0tLQoKIyDwn5OIIEJ1c2luZXNzIEluc2lnaHRzICYgQ29uY2x1c2lvbnMKCiMjIEtleSBGaW5kaW5ncyBTdW1tYXJ5CgojIyMgKipDb25jZW50cmF0aW9uIFBhdHRlcm5zOioqCjEuICoqQWxsIFVzZXJzOioqIDE1LjY4JSBhY2NvdW50IGZvciA1MCUgb2YgcmVzcG9uc2VzIOKGkiBIaWdoIGNvbmNlbnRyYXRpb24KMi4gKipSZWNlbnQgVXNlcnM6KiogMjguNjYlIGFjY291bnQgZm9yIDUwJSBvZiByZXNwb25zZXMg4oaSIEV2ZW4gaGlnaGVyIGNvbmNlbnRyYXRpb24gIAozLiAqKkVhcmx5IFRocmVzaG9sZDoqKiBPbmx5IDIuMDclIG9mIHVzZXJzIGFjY291bnQgZm9yIDEwJSBvZiByZXNwb25zZXMKCiMjIyAqKlN0cmF0ZWdpYyBJbXBsaWNhdGlvbnM6KioKLSAqKlN1cnZleSBiaWFzIHJpc2s6KiogU21hbGwgdXNlciBzdWJzZXQgZHJpdmVzIG1ham9yaXR5IG9mIGZlZWRiYWNrCi0gKipFbmdhZ2VtZW50IG9wcG9ydHVuaXR5OioqIExhcmdlIHBvcnRpb24gb2YgdXNlcnMgdW5kZXJ1dGlsaXplZCAgCi0gKipRdWFsaXR5IGZvY3VzOioqIEhlYXZ5IHVzZXJzIHJlcXVpcmUgc3BlY2lhbCBhdHRlbnRpb24gZm9yIGRhdGEgcXVhbGl0eQotICoqR3Jvd3RoIHBvdGVudGlhbDoqKiBSZWNlbnQgdXNlcnMgc2hvdyBoaWdoZXIgZW5nYWdlbWVudCByYXRlcwoKIyMjICoqVGVjaG5pY2FsIFZhbGlkYXRpb246KioKLSAqKkZvcm11bGEgYWNjdXJhY3k6KiogYG1pbih3aGljaCgpKWAgY29ycmVjdGx5IGlkZW50aWZpZXMgdGhyZXNob2xkIGNyb3NzaW5ncwotICoqRGF0YSBpbnRlZ3JpdHk6KiogUHJvcGVyIGhhbmRsaW5nIG9mIG1pc3NpbmcgSURzIGVuc3VyZXMgdmFsaWQgdXNlciBhdHRyaWJ1dGlvbgotICoqVmlzdWFsaXphdGlvbiBjbGFyaXR5OioqIExvcmVueiBjdXJ2ZSBlZmZlY3RpdmVseSBzaG93cyBjb25jZW50cmF0aW9uIHBhdHRlcm5zCgojIyBGaWxlIE91dHB1dHMgR2VuZXJhdGVkCgoqKk91dHB1dCBTdHJ1Y3R1cmU6KioKYGBgCnRhc2syL291dHB1dHMvCuKUnOKUgOKUgCBhbGxfdXNlcnNfYW5hbHlzaXMuY3N2ICAgICAgICAgIyBEZXRhaWxlZCB1c2VyLWxldmVsIGFuYWx5c2lzCuKUnOKUgOKUgCByZWNlbnRfdXNlcnNfYW5hbHlzaXMuY3N2ICAgICAgIyA5MC1kYXkgY29ob3J0IGFuYWx5c2lzICAK4pSc4pSA4pSAIGRlbnNpdHlfdGhyZXNob2xkc19zdW1tYXJ5LmNzdiAjIE11bHRpcGxlIHRocmVzaG9sZCByZXN1bHRzCuKUnOKUgOKUgCBhbGxfdnNfcmVjZW50X2NvbXBhcmlzb24uY3N2ICAgIyBDb21wYXJhdGl2ZSBhbmFseXNpcwrilJzilIDilIAgbG9yZW56X2N1cnZlX2FsbF91c2Vycy5wbmcgICAgICMgTG9yZW56IGN1cnZlIHZpc3VhbGl6YXRpb24K4pSU4pSA4pSAIHVzZXJfY29tcGFyaXNvbi5wbmcgICAgICAgICAgICAjIEFsbCB2cyByZWNlbnQgY29tcGFyaXNvbiBwbG90CmBgYAoKLS0tCgojIPCfkrsgQ29kZSBJbXBsZW1lbnRhdGlvbgoKQmVsb3cgaXMgdGhlIGNvbXBsZXRlIHRlY2huaWNhbCBpbXBsZW1lbnRhdGlvbiBvZiB0aGUgYW5hbHlzaXMgZGVzY3JpYmVkIGFib3ZlLgoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UpCgojIExvYWQgbGlicmFyaWVzCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShyZWFkcikKbGlicmFyeShwbG90bHkpCmxpYnJhcnkoc2NhbGVzKQoKIyBEZWZpbmUgcmVsYXRpdmUgcGF0aHMgZnJvbSB0YXNrMiBmb2xkZXIKZGF0YV9kaXIgPC0gIi4uLy4uL2RhdGEiICAjIEdvIHVwIHR3byBsZXZlbHMgdG8gcmVhY2ggcHJvamVjdCByb290LCB0aGVuIGludG8gZGF0YQpvdXRwdXRfZGlyIDwtICIuL291dHB1dHMiICMgQ3JlYXRlIG91dHB1dHMgZm9sZGVyIHdpdGhpbiB0YXNrMgoKIyBDcmVhdGUgb3V0cHV0IGZvbGRlciBpZiBpdCBkb2Vzbid0IGV4aXN0CmlmICghZGlyLmV4aXN0cyhvdXRwdXRfZGlyKSkgewogIGRpci5jcmVhdGUob3V0cHV0X2RpciwgcmVjdXJzaXZlID0gVFJVRSkKfQoKIyBMb2FkIGRhdGFzZXRzCmZ1bGxfZGIgPC0gcmVhZFJEUyhmaWxlLnBhdGgoZGF0YV9kaXIsICJmdWxsLXJlc3BvbnNlLWRiLnJkcyIpKQp1c2VycyA8LSByZWFkUkRTKGZpbGUucGF0aChkYXRhX2RpciwgInVzZXJzLnJkcyIpKQpgYGAKCmBgYHtyIGRhdGEtY2xlYW5pbmd9CmNhdCgiPT09IERBVEEgQ0xFQU5JTkcgJiBQUkVQQVJBVElPTiA9PT1cbiIpCgojIENoZWNrIG1pc3NpbmcgdmFsdWVzCmNhdCgiTWlzc2luZyBJRHMgaW4gcmVzcG9uc2UgZGF0YWJhc2U6Iiwgc3VtKGlzLm5hKGZ1bGxfZGIkSUQpKSwgInJvd3NcbiIpCmNhdCgiVGhlc2UgbXVzdCBiZSByZW1vdmVkIGJlY2F1c2Ugd2UgY2Fubm90IGF0dHJpYnV0ZSByZXNwb25zZXMgdG8gdXNlcnMgd2l0aG91dCBJRHNcblxuIikKCiMgUmVtb3ZlIHJvd3Mgd2l0aCBtaXNzaW5nIHVzZXIgSUQgKGNhbm5vdCBiZSBhdHRyaWJ1dGVkIHRvIGFueSB1c2VyKQpmdWxsX2RiX2NsZWFuIDwtIGZ1bGxfZGIgJT4lCiAgZmlsdGVyKCFpcy5uYShJRCkpCgpjYXQoIkFmdGVyIGNsZWFuaW5nIC0gdXNhYmxlIHJlc3BvbnNlczoiLCBucm93KGZ1bGxfZGJfY2xlYW4pLCAiXG4iKQpgYGAKCmBgYHtyIHRhc2syLTEtaW1wbGVtZW50YXRpb259CmNhdCgiPT09IFRBU0sgMi4xOiBBTEwgVVNFUlMgQU5BTFlTSVMgPT09XG4iKQoKIyBTdGVwIDE6IENvdW50IHJlc3BvbnNlcyBwZXIgdXNlcgp1c2VyX2NvdW50cyA8LSBmdWxsX2RiX2NsZWFuICU+JQogIGNvdW50KElELCBuYW1lID0gInJlc3BvbnNlX2NvdW50IikKCiMgU3RlcCAyOiBKb2luIHdpdGggdXNlciBkYXRhIGFuZCBjYWxjdWxhdGUgY3VtdWxhdGl2ZSBzdGF0aXN0aWNzCnVzZXJfZGF0YSA8LSB1c2VyX2NvdW50cyAlPiUKICBsZWZ0X2pvaW4odXNlcnMsIGJ5ID0gIklEIikgJT4lCiAgYXJyYW5nZShkZXNjKHJlc3BvbnNlX2NvdW50KSkgJT4lICMgU29ydCBieSBoaWdoZXN0IHJlc3BvbmRlcnMgZmlyc3QKICBtdXRhdGUoCiAgICAjIENhbGN1bGF0ZSBydW5uaW5nIHRvdGFscwogICAgY3VtdWxhdGl2ZV9yZXNwb25zZXMgPSBjdW1zdW0ocmVzcG9uc2VfY291bnQpLAogICAgdG90YWxfcmVzcG9uc2VzID0gc3VtKHJlc3BvbnNlX2NvdW50KSwKICAgIGN1bXVsYXRpdmVfcGN0ID0gY3VtdWxhdGl2ZV9yZXNwb25zZXMgLyB0b3RhbF9yZXNwb25zZXMsCiAgICB1c2VyX3JhbmsgPSByb3dfbnVtYmVyKCksCiAgICB1c2VyX3BjdCA9IHVzZXJfcmFuayAvIG4oKSAjIFBlcmNlbnRhZ2Ugb2YgdXNlcnMgcmVwcmVzZW50ZWQKICApCgojIFN0ZXAgMzogRmluZCBtaW5pbXVtIHVzZXJzIGZvciA1MCUgdGhyZXNob2xkIHVzaW5nIG1pbih3aGljaCgpKQpjdXRvZmZfcm93IDwtIG1pbih3aGljaCh1c2VyX2RhdGEkY3VtdWxhdGl2ZV9wY3QgPj0gMC41KSkKcGN0XzUwX2FsbCA8LSB1c2VyX2RhdGEkdXNlcl9wY3RbY3V0b2ZmX3Jvd10gKiAxMDAKCmNhdChzcHJpbnRmKCJBTlNXRVI6ICUuMmYlJSBvZiB1c2VycyBhY2NvdW50IGZvciA1MCUlIG9mIGFsbCByZXNwb25zZXNcbiIsIHBjdF81MF9hbGwpKQoKIyBTYXZlIGRldGFpbGVkIGFuYWx5c2lzCndyaXRlX2Nzdih1c2VyX2RhdGEsIGZpbGUucGF0aChvdXRwdXRfZGlyLCAiYWxsX3VzZXJzX2FuYWx5c2lzLmNzdiIpKQpgYGAKCmBgYHtyIHRhc2syLTItaW1wbGVtZW50YXRpb259CmNhdCgiPT09IFRBU0sgMi4yOiBSRUNFTlQgVVNFUlMgKDkwIERBWVMpID09PVxuIikKCiMgU3RlcCAxOiBQYXJzZSBzaWdudXAgZGF0ZXMgYW5kIGRlZmluZSA5MC1kYXkgd2luZG93CnVzZXJzJHNpZ251cF9kYXRlIDwtIGFzLkRhdGUodXNlcnMkc2lnbnVwX2RhdGUsIGZvcm1hdCA9ICIlbS8lZC8lWSIpCmN1dG9mZl9kYXRlIDwtIG1heCh1c2VycyRzaWdudXBfZGF0ZSwgbmEucm0gPSBUUlVFKSAtIDkwCmNhdCgiOTAtZGF5IGN1dG9mZiBkYXRlOiIsIGFzLmNoYXJhY3RlcihjdXRvZmZfZGF0ZSksICJcbiIpCgojIFN0ZXAgMjogRmlsdGVyIHRvIHJlY2VudCB1c2VycyBvbmx5CnJlY2VudF91c2VycyA8LSB1c2VycyAlPiUKICBmaWx0ZXIoc2lnbnVwX2RhdGUgPj0gY3V0b2ZmX2RhdGUpCmNhdCgiUmVjZW50IHVzZXJzIGZvdW5kOiIsIG5yb3cocmVjZW50X3VzZXJzKSwgIlxuIikKCiMgU3RlcCAzOiBTdWJzZXQgcmVzcG9uc2UgZGF0YSBmb3IgcmVjZW50IHVzZXJzCnJlY2VudF9kYXRhIDwtIHVzZXJfZGF0YSAlPiUKICBmaWx0ZXIoSUQgJWluJSByZWNlbnRfdXNlcnMkSUQpICU+JQogIGFycmFuZ2UoZGVzYyhyZXNwb25zZV9jb3VudCkpICU+JQogIG11dGF0ZSgKICAgICMgUmVjYWxjdWxhdGUgY3VtdWxhdGl2ZSBzdGF0cyBmb3IgcmVjZW50IGNvaG9ydCBvbmx5CiAgICBjdW11bGF0aXZlX3Jlc3BvbnNlcyA9IGN1bXN1bShyZXNwb25zZV9jb3VudCksCiAgICB0b3RhbF9yZXNwb25zZXMgPSBzdW0ocmVzcG9uc2VfY291bnQpLAogICAgY3VtdWxhdGl2ZV9wY3QgPSBjdW11bGF0aXZlX3Jlc3BvbnNlcyAvIHRvdGFsX3Jlc3BvbnNlcywKICAgIHVzZXJfcmFuayA9IHJvd19udW1iZXIoKSwKICAgIHVzZXJfcGN0ID0gdXNlcl9yYW5rIC8gbigpCiAgKQoKIyBTdGVwIDQ6IEZpbmQgNTAlIHRocmVzaG9sZCBmb3IgcmVjZW50IHVzZXJzCmN1dG9mZl9yb3dfcmVjZW50IDwtIG1pbih3aGljaChyZWNlbnRfZGF0YSRjdW11bGF0aXZlX3BjdCA+PSAwLjUpKQpwY3RfNTBfcmVjZW50IDwtIHJlY2VudF9kYXRhJHVzZXJfcGN0W2N1dG9mZl9yb3dfcmVjZW50XSAqIDEwMAoKY2F0KHNwcmludGYoIkFOU1dFUjogJS4yZiUlIG9mIHJlY2VudCB1c2VycyBhY2NvdW50IGZvciA1MCUlIG9mIHRoZWlyIHJlc3BvbnNlc1xuIiwgcGN0XzUwX3JlY2VudCkpCgojIFNhdmUgcmVjZW50IHVzZXJzIGFuYWx5c2lzCndyaXRlX2NzdihyZWNlbnRfZGF0YSwgZmlsZS5wYXRoKG91dHB1dF9kaXIsICJyZWNlbnRfdXNlcnNfYW5hbHlzaXMuY3N2IikpCgojIENvbXBhcmlzb24gaW5zaWdodApjYXQoIlxuQ09NUEFSSVNPTjpcbiIpCmNhdCgiQWxsIHVzZXJzOiIsIHJvdW5kKHBjdF81MF9hbGwsIDIpLCAiJVxuIikKY2F0KCJSZWNlbnQgdXNlcnM6Iiwgcm91bmQocGN0XzUwX3JlY2VudCwgMiksICIlXG4iKQppZihwY3RfNTBfcmVjZW50IDwgcGN0XzUwX2FsbCkgewogIGNhdCgiUmVjZW50IHVzZXJzIHNob3cgSElHSEVSIGNvbmNlbnRyYXRpb24gKG1vcmUgYWN0aXZlIGhlYXZ5IHVzZXJzKVxuIikKfSBlbHNlIHsKICBjYXQoIlJlY2VudCB1c2VycyBzaG93IExPV0VSIGNvbmNlbnRyYXRpb24gKG1vcmUgZGlzdHJpYnV0ZWQgZW5nYWdlbWVudClcbiIpCn0KYGBgCgpgYGB7ciB0YXNrMi0zLWltcGxlbWVudGF0aW9ufQpjYXQoIj09PSBUQVNLIDIuMzogTVVMVElQTEUgVEhSRVNIT0xEIEFOQUxZU0lTID09PVxuIikKCiMgQ2FsY3VsYXRlIHVzZXIgcGVyY2VudGFnZXMgbmVlZGVkIGZvciB2YXJpb3VzIHJlc3BvbnNlIHRocmVzaG9sZHMKcmVzcG9uc2VfdGhyZXNob2xkcyA8LSBjKDAuMTAsIDAuMjAsIDAuMzAsIDAuNDAsIDAuNTAsIDAuNjAsIDAuNzAsIDAuODAsIDAuOTApCgpkZW5zaXR5X3N1bW1hcnkgPC0gc2FwcGx5KHJlc3BvbnNlX3RocmVzaG9sZHMsIGZ1bmN0aW9uKHRocmVzaCkgewogIGN1dG9mZl9pbmRleCA8LSBtaW4od2hpY2godXNlcl9kYXRhJGN1bXVsYXRpdmVfcGN0ID49IHRocmVzaCkpCiAgdXNlcl9kYXRhJHVzZXJfcGN0W2N1dG9mZl9pbmRleF0gKiAxMDAKfSkKCm5hbWVzKGRlbnNpdHlfc3VtbWFyeSkgPC0gcGFzdGUwKHJlc3BvbnNlX3RocmVzaG9sZHMgKiAxMDAsICIlIikKCmNhdCgiVXNlciBwZXJjZW50YWdlIG5lZWRlZCBmb3IgZWFjaCByZXNwb25zZSB0aHJlc2hvbGQ6XG4iKQpmb3IgKGkgaW4gMTpsZW5ndGgoZGVuc2l0eV9zdW1tYXJ5KSkgewogIGNhdChzcHJpbnRmKCLigKIgJS4yZiUlIG9mIHVzZXJzIOKGkiAlcyBvZiByZXNwb25zZXNcbiIsIAogICAgICAgICAgICAgIGRlbnNpdHlfc3VtbWFyeVtpXSwgbmFtZXMoZGVuc2l0eV9zdW1tYXJ5KVtpXSkpCn0KCiMgQ3JlYXRlIHN1bW1hcnkgdGFibGUKc3VtbWFyeV90YWJsZSA8LSBkYXRhLmZyYW1lKAogIFJlc3BvbnNlX1RocmVzaG9sZCA9IG5hbWVzKGRlbnNpdHlfc3VtbWFyeSksCiAgVXNlcl9QZXJjZW50YWdlX05lZWRlZCA9IHJvdW5kKGRlbnNpdHlfc3VtbWFyeSwgMikKKQoKd3JpdGVfY3N2KHN1bW1hcnlfdGFibGUsIGZpbGUucGF0aChvdXRwdXRfZGlyLCAiZGVuc2l0eV90aHJlc2hvbGRzX3N1bW1hcnkuY3N2IikpCmBgYAoKYGBge3IgbG9yZW56LWN1cnZlLXZpc3VhbGl6YXRpb259CmNhdCgiPT09IExPUkVOWiBDVVJWRSBWSVNVQUxJWkFUSU9OID09PVxuIikKCiMgQ3JlYXRlIExvcmVueiBjdXJ2ZSBwbG90CmxvcmVuel9wbG90IDwtIGdncGxvdCh1c2VyX2RhdGEsIGFlcyh4ID0gdXNlcl9wY3QsIHkgPSBjdW11bGF0aXZlX3BjdCkpICsKICBnZW9tX2xpbmUoY29sb3IgPSAiZGFya2JsdWUiLCBzaXplID0gMS4yLCBhbHBoYSA9IDAuOCkgKwogIGdlb21fYWJsaW5lKGludGVyY2VwdCA9IDAsIHNsb3BlID0gMSwgbGluZXR5cGUgPSAiZGFzaGVkIiwgCiAgICAgICAgICAgICAgY29sb3IgPSAiZ3JheTUwIiwgYWxwaGEgPSAwLjcpICsKICBhbm5vdGF0ZSgidGV4dCIsIHggPSAwLjcsIHkgPSAwLjMsIAogICAgICAgICAgIGxhYmVsID0gIlBlcmZlY3QgRXF1YWxpdHlcbig0NcKwIGxpbmUpIiwgCiAgICAgICAgICAgY29sb3IgPSAiZ3JheTUwIiwgc2l6ZSA9IDMpICsKICBhbm5vdGF0ZSgidGV4dCIsIHggPSAwLjMsIHkgPSAwLjcsIAogICAgICAgICAgIGxhYmVsID0gIkFjdHVhbCBEaXN0cmlidXRpb25cbihIaWdoIENvbmNlbnRyYXRpb24pIiwgCiAgICAgICAgICAgY29sb3IgPSAiZGFya2JsdWUiLCBzaXplID0gMykgKwogIHNjYWxlX3hfY29udGludW91cyhsYWJlbHMgPSBzY2FsZXM6OnBlcmNlbnQsIG5hbWUgPSAiQ3VtdWxhdGl2ZSAlIG9mIFVzZXJzIikgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBzY2FsZXM6OnBlcmNlbnQsIG5hbWUgPSAiQ3VtdWxhdGl2ZSAlIG9mIFJlc3BvbnNlcyIpICsKICBsYWJzKHRpdGxlID0gIlJlc3BvbnNlIERlbnNpdHk6IExvcmVueiBDdXJ2ZSBBbmFseXNpcyIsCiAgICAgICBzdWJ0aXRsZSA9IHBhc3RlMCgiRGlzdGFuY2UgZnJvbSBkaWFnb25hbCBzaG93cyBjb25jZW50cmF0aW9uIGxldmVsIHwgIiwKICAgICAgICAgICAgICAgICAgICAgICAgcm91bmQocGN0XzUwX2FsbCwgMSksICIlIG9mIHVzZXJzIOKGkiA1MCUgb2YgcmVzcG9uc2VzIikpICsKICB0aGVtZV9taW5pbWFsKGJhc2Vfc2l6ZSA9IDEyKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGZhY2UgPSAiYm9sZCIpKQoKIyBEaXNwbGF5IGludGVyYWN0aXZlIHZlcnNpb24KaW50ZXJhY3RpdmVfbG9yZW56IDwtIGdncGxvdGx5KGxvcmVuel9wbG90LCB0b29sdGlwID0gYygieCIsICJ5IikpCmludGVyYWN0aXZlX2xvcmVuegoKIyBTYXZlIHN0YXRpYyB2ZXJzaW9uCmdnc2F2ZShmaWxlLnBhdGgob3V0cHV0X2RpciwgImxvcmVuel9jdXJ2ZV9hbGxfdXNlcnMucG5nIiksIGxvcmVuel9wbG90LCAKICAgICAgIHdpZHRoID0gMTAsIGhlaWdodCA9IDYsIGRwaSA9IDMwMCkKCmNhdCgiTG9yZW56IEN1cnZlIEludGVycHJldGF0aW9uOlxuIikKY2F0KCLigKIgRGlhZ29uYWwgbGluZSA9IFBlcmZlY3QgZXF1YWxpdHkgKGV2ZXJ5b25lIGNvbnRyaWJ1dGVzIGVxdWFsbHkpXG4iKQpjYXQoIuKAoiBDdXJ2ZWQgbGluZSA9IEFjdHVhbCBkaXN0cmlidXRpb24gKGNvbmNlbnRyYXRlZCBhbW9uZyBmZXcgdXNlcnMpXG4iKQpjYXQoIuKAoiBMYXJnZXIgYXJlYSBiZXR3ZWVuIGxpbmVzID0gSGlnaGVyIGNvbmNlbnRyYXRpb25cbiIpCmBgYAoKYGBge3IgY29tcGFyaXNvbi1hbGwtdnMtcmVjZW50fQpjYXQoIj09PSBDT01QQVJJU09OOiBBTEwgdnMgUkVDRU5UIFVTRVJTID09PVxuIikKCiMgQ2FsY3VsYXRlIHRocmVzaG9sZHMgZm9yIHJlY2VudCB1c2VycwpyZWNlbnRfc3VtbWFyeSA8LSBzYXBwbHkocmVzcG9uc2VfdGhyZXNob2xkcywgZnVuY3Rpb24odGhyZXNoKSB7CiAgaWYgKG1heChyZWNlbnRfZGF0YSRjdW11bGF0aXZlX3BjdCkgPj0gdGhyZXNoKSB7CiAgICBjdXRvZmZfaW5kZXggPC0gbWluKHdoaWNoKHJlY2VudF9kYXRhJGN1bXVsYXRpdmVfcGN0ID49IHRocmVzaCkpCiAgICByZWNlbnRfZGF0YSR1c2VyX3BjdFtjdXRvZmZfaW5kZXhdICogMTAwCiAgfSBlbHNlIHsKICAgIE5BCiAgfQp9KQoKIyBDcmVhdGUgY29tcGFyaXNvbiBkYXRhc2V0CmNvbXBhcmlzb25fZGF0YSA8LSBkYXRhLmZyYW1lKAogIFJlc3BvbnNlX1RocmVzaG9sZCA9IHJlc3BvbnNlX3RocmVzaG9sZHMgKiAxMDAsCiAgQWxsX1VzZXJzID0gZGVuc2l0eV9zdW1tYXJ5LAogIFJlY2VudF9Vc2VycyA9IHJlY2VudF9zdW1tYXJ5CikgJT4lCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBjKEFsbF9Vc2VycywgUmVjZW50X1VzZXJzKSwgCiAgICAgICAgICAgICAgIG5hbWVzX3RvID0gIlVzZXJfR3JvdXAiLCAKICAgICAgICAgICAgICAgdmFsdWVzX3RvID0gIlVzZXJfUGVyY2VudGFnZSIpCgojIENyZWF0ZSBjb21wYXJpc29uIHBsb3QKY29tcGFyaXNvbl9wbG90IDwtIGdncGxvdChjb21wYXJpc29uX2RhdGEsIGFlcyh4ID0gUmVzcG9uc2VfVGhyZXNob2xkLCB5ID0gVXNlcl9QZXJjZW50YWdlLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gVXNlcl9Hcm91cCwgbGluZXR5cGUgPSBVc2VyX0dyb3VwKSkgKwogIGdlb21fbGluZShzaXplID0gMS4yKSArCiAgZ2VvbV9wb2ludChzaXplID0gMykgKwogIHNjYWxlX3hfY29udGludW91cyhuYW1lID0gIlJlc3BvbnNlIFRocmVzaG9sZCAoJSkiLCBicmVha3MgPSBzZXEoMTAsIDkwLCAxMCkpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobmFtZSA9ICJVc2VyIFBlcmNlbnRhZ2UgTmVlZGVkICglKSIpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiQWxsX1VzZXJzIiA9ICJkYXJrYmx1ZSIsICJSZWNlbnRfVXNlcnMiID0gImRhcmtyZWQiKSkgKwogIGxhYnModGl0bGUgPSAiUmVzcG9uc2UgQ29uY2VudHJhdGlvbjogQWxsIFVzZXJzIHZzIFJlY2VudCBVc2VycyAoOTAgRGF5cykiLAogICAgICAgc3VidGl0bGUgPSAiTG93ZXIgdmFsdWVzIGluZGljYXRlIGhpZ2hlciBjb25jZW50cmF0aW9uIGFtb25nIGZld2VyIHVzZXJzIiwKICAgICAgIGNvbG9yID0gIlVzZXIgR3JvdXAiLCBsaW5ldHlwZSA9ICJVc2VyIEdyb3VwIikgKwogIHRoZW1lX21pbmltYWwoYmFzZV9zaXplID0gMTIpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwgZmFjZSA9ICJib2xkIiksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpCgpwcmludChjb21wYXJpc29uX3Bsb3QpCmdnc2F2ZShmaWxlLnBhdGgob3V0cHV0X2RpciwgInVzZXJfY29tcGFyaXNvbi5wbmciKSwgY29tcGFyaXNvbl9wbG90LCAKICAgICAgIHdpZHRoID0gMTAsIGhlaWdodCA9IDYsIGRwaSA9IDMwMCkKCiMgU2F2ZSBjb21wYXJpc29uIGRhdGEKd3JpdGVfY3N2KGNvbXBhcmlzb25fZGF0YSwgZmlsZS5wYXRoKG91dHB1dF9kaXIsICJhbGxfdnNfcmVjZW50X2NvbXBhcmlzb24uY3N2IikpCmBgYAoKYGBge3IgZmluYWwtc3VtbWFyeX0KY2F0KCI9PT0gQU5BTFlTSVMgQ09NUExFVEUgPT09XG4iKQpjYXQoIkdlbmVyYXRlZCBmaWxlcyBpbiIsIG5vcm1hbGl6ZVBhdGgob3V0cHV0X2RpciksICI6XG4iKQpvdXRwdXRfZmlsZXMgPC0gbGlzdC5maWxlcyhvdXRwdXRfZGlyKQpmb3IgKGZpbGUgaW4gb3V0cHV0X2ZpbGVzKSB7CiAgY2F0KCLigKIiLCBmaWxlLCAiXG4iKQp9CgpjYXQoIlxuRmluYWwgQW5zd2VyczpcbiIpCmNhdCgiVGFzayAyLjE6Iiwgcm91bmQocGN0XzUwX2FsbCwgMiksICIlIG9mIGFsbCB1c2VycyDihpIgNTAlIG9mIHJlc3BvbnNlc1xuIikKY2F0KCJUYXNrIDIuMjoiLCByb3VuZChwY3RfNTBfcmVjZW50LCAyKSwgIiUgb2YgcmVjZW50IHVzZXJzIOKGkiA1MCUgb2YgdGhlaXIgcmVzcG9uc2VzXG4iKQpjYXQoIlRhc2sgMi4zOiBMb3JlbnogY3VydmUgdmlzdWFsaXphdGlvbiB3aXRoIG11bHRpcGxlIHRocmVzaG9sZCBhbmFseXNpc1xuIikKYGBgIA==